package com.stars.easyms.logging.trace;

import com.plumelog.core.LogMessageThreadLocal;
import com.plumelog.core.MessageAppenderFactory;
import com.plumelog.core.TraceMessage;
import com.plumelog.core.constant.LogMessageConstant;
import com.plumelog.core.util.GfJsonUtil;
import com.plumelog.core.util.TraceLogMessageFactory;
import com.stars.easyms.base.interceptor.AbstractEasyMsMethodInterceptor;
import com.stars.easyms.base.trace.EasyMsTraceHelper;
import com.stars.easyms.base.util.ReflectUtil;
import com.stars.easyms.logging.constant.EasyMsLoggingConstants;
import com.stars.easyms.logging.properties.EasyMsLoggingPlumeLogHelper;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInvocation;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>className: EasyMsLoggingPlumeLogTraceAspect</p>
 * <p>description: EasyMsPlumeLogTrace拦截器</p>
 *
 * @author guoguifang
 * @version 1.7.0
 * @date 2020/11/17 2:05 下午
 */
@Slf4j
final class EasyMsLoggingPlumeLogTraceAspect extends AbstractEasyMsMethodInterceptor {

    private static final AtomicInteger GLOBAL_TRACE_NUM = new AtomicInteger();

    @Override
    protected Object intercept(MethodInvocation methodInvocation) throws Throwable {
        return EasyMsTraceHelper.setTraceIdIfNecessary(traceId -> {
            if (!log.isDebugEnabled() && !EasyMsLoggingPlumeLogHelper.isAppenderEnabled()) {
                return proceed(methodInvocation);
            }

            boolean isNewTraceMessage = false;
            TraceMessage traceMessage = LogMessageThreadLocal.logMessageThreadLocal.get();
            if (traceMessage == null) {
                traceMessage = new TraceMessage();
                isNewTraceMessage = true;
                LogMessageThreadLocal.logMessageThreadLocal.set(traceMessage);
            }
            String methodName = ReflectUtil.getMethodFullName(methodInvocation.getMethod());
            try {
                traceMessage.setTraceId(traceId);
                traceMessage.setMessageType(methodName);
                traceMessage.setPosition(EasyMsLoggingConstants.PLUME_LOG_METHOD_ENTRY_POSITION);
                traceMessage.getPositionNum().set(GLOBAL_TRACE_NUM.incrementAndGet());
                pushTraceMessage(traceMessage, true);
                Object proceed = proceed(methodInvocation);
                traceMessage.setMessageType(methodName);
                traceMessage.setPosition(EasyMsLoggingConstants.PLUME_LOG_METHOD_EXIT_POSITION);
                traceMessage.getPositionNum().set(GLOBAL_TRACE_NUM.incrementAndGet());
                pushTraceMessage(traceMessage, false);
                return proceed;
            } finally {
                if (isNewTraceMessage) {
                    LogMessageThreadLocal.logMessageThreadLocal.remove();
                }
            }
        });
    }

    private void pushTraceMessage(TraceMessage traceMessage, boolean isEntry) {
        if (log.isDebugEnabled()) {
            log.debug("{} {} method '{}'!", LogMessageConstant.TRACE_PRE, isEntry ? "Entry" : "Exit", traceMessage.getMessageType());
        }
        if (EasyMsLoggingPlumeLogHelper.isAppenderEnabled()) {
            MessageAppenderFactory.pushTracedataQueue(
                    GfJsonUtil.toJSONString(
                            TraceLogMessageFactory.getTraceLogMessage(
                                    traceMessage, EasyMsLoggingPlumeLogHelper.getAppName(), System.currentTimeMillis())));
        }
    }

}